﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Xml.Serialization;
using Newtonsoft.Json;

namespace Common
{
    /// <summary>
    /// 序列化辅助类
    /// </summary>
    public static class SerializeHelper
    {
        #region 二进制序列化

        public static byte[] SerializeToBinary(object obj)
        {
            var b = new byte[2500];
            var ms = new MemoryStream();

            try
            {
                var bformatter = new BinaryFormatter();
                bformatter.Serialize(ms, obj);
                ms.Seek(0, 0);
                if (ms.Length > b.Length) b = new byte[ms.Length];
                b = ms.ToArray();
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                ms.Close();
            }
            return b;
        }

        public static void SerializeToBinary(object obj, string path, FileMode mode)
        {
            var fs = new FileStream(path, mode);

            // Construct a BinaryFormatter and use it to serialize the data to the stream.
            var formatter = new BinaryFormatter();
            try
            {
                formatter.Serialize(fs, obj);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
        }

        public static void SerializeToBinary(object obj, string path)
        {
            SerializeToBinary(obj, path, FileMode.Create);
        }

        public static string SerializeToBin(object obj)
        {
            IFormatter formatter = new BinaryFormatter();
            var ms = new MemoryStream();
            formatter.Serialize(ms, obj);
            ms.Seek(0, SeekOrigin.Begin);
            var bt = new byte[ms.Length];
            ms.Read(bt, 0, bt.Length);
            ms.Close();
            return Convert.ToBase64String(bt);
        }

        #endregion

        #region SOAP 序列化

        public static string SerializeToSoap(object obj)
        {
            string s = "";
            var ms = new MemoryStream();

            try
            {
                var sformatter = new SoapFormatter();
                sformatter.Serialize(ms, obj);
                ms.Seek(0, 0);
                s = Encoding.ASCII.GetString(ms.ToArray());
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                ms.Close();
            }

            return s;
        }

        public static void SerializeToSoap(object obj, string path, FileMode mode)
        {
            var fs = new FileStream(path, mode);

            // Construct a BinaryFormatter and use it to serialize the data to the stream.
            var formatter = new SoapFormatter();
            try
            {
                formatter.Serialize(fs, obj);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
        }

        public static void SerializeToSoap(object obj, string path)
        {
            SerializeToSoap(obj, path, FileMode.Create);
        }

        #endregion

        #region XML 序列化

        public static string SerializeToXml(object obj)
        {
            string s = "";
            var ms = new MemoryStream();

            try
            {
                var serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(ms, obj);
                ms.Seek(0, 0);
                s = Encoding.ASCII.GetString(ms.ToArray());
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                ms.Close();
            }
            return s;
        }

        public static void SerializeToXmlFile(object obj, string path, FileMode mode)
        {
            var fs = new FileStream(path, mode);

            // Construct a BinaryFormatter and use it to serialize the data to the stream.
            var serializer = new XmlSerializer(obj.GetType());
            try
            {
                serializer.Serialize(fs, obj);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
        }

        public static void SerializeToXmlFile(object obj, string path)
        {
            SerializeToXmlFile(obj, path, FileMode.Create);
        }

        #endregion

        #region XML 反序列化

        public static object DeserializeFromXmlFile(Type type, string path)
        {
            var o = new object();
            var fs = new FileStream(path, FileMode.Open);

            try
            {
                var serializer = new XmlSerializer(type);
                o = serializer.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
            return o;
        }

        public static object DeserializeFromXml(Type type, string s)
        {
            var o = new object();

            try
            {
                var serializer = new XmlSerializer(type);
                o = serializer.Deserialize(new StringReader(s));
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
            }
            return o;
        }

        #endregion

        #region SOAP 反序列化

        public static object DeserializeFromSoap(Type type, string s)
        {
            var o = new object();
            var ms = new MemoryStream(new UTF8Encoding().GetBytes(s));

            try
            {
                var serializer = new SoapFormatter();
                o = serializer.Deserialize(ms);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
            }
            return o;
        }

        #endregion

        #region 二进制反序列化

        public static object DeserializeFromBinary(Type type, byte[] bytes)
        {
            var o = new object();
            var ms = new MemoryStream(bytes);

            try
            {
                var serializer = new BinaryFormatter();
                o = serializer.Deserialize(ms);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
            }
            return o;
        }

        public static object DeserializeFromBinary(Type type, string path)
        {
            var o = new object();
            var fs = new FileStream(path, FileMode.Open);

            try
            {
                var serializer = new BinaryFormatter();
                o = serializer.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
            return o;
        }

        public static object DeserializeFromBinary(string strBase64)
        {
            object obj = null;
            IFormatter formatter = new BinaryFormatter();
            byte[] bt = Convert.FromBase64String(strBase64);
            var ms = new MemoryStream(bt);
            obj = formatter.Deserialize(ms);
            ms.Close();
            return obj;
        }

        private static long GetByteSize(object o)
        {
            var bFormatter = new BinaryFormatter();
            var stream = new MemoryStream();
            bFormatter.Serialize(stream, o);
            return stream.Length;
        }

        #endregion

        #region Josn 序列化/反序列化

        /// <summary>
        ///     Serialize POCO to a binary stream.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static byte[] JsonSerialize<T>(T obj)
        {
            return Encoding.UTF8.GetBytes(
                JsonConvert.SerializeObject(obj, Formatting.None));
        }

        /// <summary>
        ///     Deserialize POCO from a binary stream.
        /// </summary>
        /// <typeparam name="T">expected type of the POCO.</typeparam>
        /// <param name="buffer">Stream containing the POCO.</param>
        /// <returns>Deserialized object corresponding to the POCO.</returns>
        public static T JsonDeserialize<T>(byte[] buffer)
        {
            string text = Encoding.UTF8.GetString(buffer);
            return JsonConvert.DeserializeObject<T>(text);
        }

        #endregion

        #region Protobuf 序列化/反序列化

        public static byte[] ProtobufSerialize<T>(T obj)
        {
            using (var stream = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize(stream, obj);
                return stream.GetBuffer();
            }
        }

        public static T ProtobufDeserialize<T>(byte[] buffer)
        {
            using (var stream = new MemoryStream(buffer))
            {
                return ProtoBuf.Serializer.Deserialize<T>(stream);
            }
        }

        #endregion

        public static object Clone(object o)
        {
            var bFormatter = new BinaryFormatter();
            var stream = new MemoryStream();
            object cloned = null;

            try
            {
                bFormatter.Serialize(stream, o);
                stream.Seek(0, SeekOrigin.Begin);
                cloned = bFormatter.Deserialize(stream);
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                stream.Close();
            }

            return cloned;
        }
    }
}